package skipList_02;

import general_algorithms.Hashing;
import general_algorithms.Hashing_algorithms;

import java.math.BigInteger;
import java.security.NoSuchAlgorithmException;
import java.util.EmptyStackException;
import java.util.Stack;

import skipList_02.SkipList;
import skipList_02.SkipListNode;

/**
 * @author Tobias Hardes Aug 1, 2014 1:31:44 PM
 */
public class SkipListProof implements interfaces.ISkipListProof {
	/**
	 * Generates a proof of the file system to verify its integrity
	 * 
	 * @param input SkipList of the filesystem to verify
	 * @param key Some key of a file
	 * @return A skip list augmented with authenticated information to perform the verification
	 */
	public SkipList generateProof(SkipList input, int key) {
		SkipList proof = new SkipList();
		SkipListSearchResult result = input.search(key);
		Stack<SkipListNode> st = result.getSt();

		/*********************************************************
		 * 1. u = null, i.e., v is on the base level: * If w is a tower node,
		 * then f(v) = h(elem(v), elem(w)).* If w is a plateau node, then f(v) =
		 * h(elem(v), f(w)). *
		 ********************************************************/

		/*********************************************************
		 * 2. u = null, i.e., v is not on the base level: * If w is a tower
		 * node, then f(v) = f(u). * If w is a plateau node, then f(v) = h(f(u),
		 * f(w)). *
		 ********************************************************/
		SkipListNode currentNode;// First node is the element itsself
		currentNode = st.pop();
		/*byte[] digest = null;
		
		 * while(currentNode != null) { if(currentNode.level == 0)//Base node
		 * digest = value_BaseNode(currentNode); else digest =
		 * value_NonBaseNode(currentNode); try { currentNode = st.pop(); }
		 * catch(EmptyStackException ex) { currentNode = null; }
		 * 
		 * }
		 */
		while (!st.empty()) {
			st.pop();
			//Do funny things
		}

		return proof;
	}

	public boolean verifyProof(SkipList proof) {
		throw new UnsupportedOperationException();
	}
	/**
	 * Calculates the hash value for a base node --> Level of the node is 0
	 * 
	 * @param node The node of the skiplist to calculate
	 * @return Hash value of the skip list
	 */
	private byte[] value_BaseNode(SkipListNode node) {
		/*
		 * Calculate f (v) = h(elem(v), elem(w)).
		 */
		if (node.level < node.maxLevel)// Tower node --> there is a node above
		{
			return node.hashValue = calculateHash(node.key + "",
					node.next[0].key + "");
		}
		/*
		 * Calculate f(v) = h(elem(v), f (right(v))).
		 */
		else // Plateau node
		{
			System.out.println(node.next[0].key);
			return node.hashValue = calculateHash(node.key + "",
					Hashing.hashByteToString(node.next[0].hashValue));
		}

		// return Hashing.hashByteToString(node.hashValue);
	}

	/**
	 * Calculates the hash value for a non-base node --> Level of the node > 0
	 * 
	 * @param node The node of the skiplist to calculate
	 * @return Hash value of the skip list
	 */
	private byte[] value_NonBaseNode(SkipListNode node) {
		/*
		 * Calculate f (v) = f(u). ==> Take value from bottom node
		 */
		if (node.level <= node.maxLevel)// Tower node --> there is a node above
		{
			return node.hashValue = node.down[0].hashValue;
		}
		/*
		 * Calculate f(v) = h(f(down(v)), f (right(v)))
		 */
		else // Plateau node
		{
			if (node.down[0] != null)
				return node.hashValue = calculateHash(
						Hashing.hashByteToString(node.down[0].hashValue),
						Hashing.hashByteToString(node.next[0].hashValue));
			else
				return node.hashValue = calculateHash(node.key + "",
						Hashing.hashByteToString(node.next[0].hashValue));
		}

	}

	/**
	 * The real calculation of the hash
	 * 
	 * @param value1 First input for hash calculation
	 * @param value2 Second input for hash calculation
	 * @return Hash value of both inputs
	 */
	private byte[] calculateHash(String value1, String value2) {
		String result = "";
		byte[] hash = null;
		try {
			hash = Hashing.GetHash(Hashing_algorithms.SHA256, value1 + value2);
			BigInteger bigInt = new BigInteger(1, hash);
			result = bigInt.toString(16);
			System.out.println(result);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		return hash;
	}
}
